Fix memory leak in AnimatedProps constructor #2809
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes a memory leak in
AnimatedProps
that causes unbounded accumulation of child nodes in applications with frequent re-renders.Fixes #2756. Builds on recent improvements in this area like #2800.
Background
During investigation of facebook/react-native#49217, I discovered this issue is specific to react-native-web. This area of the vendored Animated implementation has diverged from React Native core, where the
AnimatedProps
constructor does not call__attach()
.Root Cause
The
AnimatedProps
constructor callsthis.__attach()
(line 35), thenuseAnimatedPropsLifecycle
callsnode.__attach()
again (line 110 inuseAnimatedProps.js
). This causes the sameAnimatedProps
instance to add itself as a child to parent nodes twice.Each re-render creates a new
AnimatedProps
with this double-attachment, but cleanup only removes one reference. The result is unbounded accumulation of duplicate children. In a stress test with 500 animated components re-rendering every 50ms, this produced 359,382 duplicate__addChild
calls.Live demo of the issue (note: Android/iOs works fine, but web version exhibits the leak)
Approach
This PR removes the constructor
__attach()
call entirely.Why this is safe:
useLayoutEffect
inuseAnimatedProps
(line 110) synchronously callsnode.__attach()
before browser paintInitially considered adding a defensive check in
__addChild()
to prevent duplicates:While this prevented the leak, it masked the underlying issue rather than addressing the root cause.
Impact